昨天我們談論了 Electron 應用程式手動更新的流程 ,
今天 , 我們就在 app 開啟時放入昨天那些手動更新的流程 ,
讓我們的 app 有自動更新的功能 (^.^)/
類型 | 流程 |
---|---|
Portal | 下載新執行檔 => 取代舊檔 |
NSIS | 下載安裝檔 => 執行安裝 |
Portal 的更新與 NSIS 的更新流程差別不大 , 因此下方本魯只介紹 NSIS 的更新流程 ,
相信厲害的邦友們 , 必定能舉一反三 , 寫出 Portal 版本的更新自動化
我們利用 download.js 輔助我們下載新版的 exe 檔案
首先 , 製作 downloadUtil.js 方便我們之後操作下載行為
// utils/downloadUtil.js
const download = require('download');
const fs = require('fs');
const _ = require('lodash');
const fileDownload = (url, dest) => {
// duplexStream is a Promise & EventEmitter
const duplexStream = download(url);
let downloadedLength = 0;
const writeStream = fs.createWriteStream(dest);
// 完成寫入檔案到指定位置
writeStream.on("finish", () => duplexStream.emit('write-finish'));
// 寫入檔案出錯時
writeStream.on("error", err => duplexStream.emit('write-error', err));
// 限制每 0.5 秒至多執行 1 次
const throttleFunc = _.throttle(func => func(), 500);
duplexStream.on('response', res => {
const totalLength = res.headers['content-length'];
res.on('data', data => {
downloadedLength += data.length;
// 因為 duplexStream 是 EventEmitter 所以 emit channel : "got-data"
const params = {data, downloadedLength, totalLength};
throttleFunc(() => duplexStream.emit('got-data', params));
});
});
duplexStream.on("error", err => console.error(err));
duplexStream.pipe(writeStream);
// duplexStream.pause(); // 下載暫停
// duplexStream.resume(); // 下載繼續
return duplexStream;
}
module.exports = fileDownload;
如何使用 downloadUtil.js ?
const doDownload = (url, dest, cb) => {
return new Promise((resolve, reject) => {
downloadUtil(url, dest)
.on('got-data', ({downloadedLength, totalLength}) => {
const saved = new Intl.NumberFormat().format(downloadedLength);
const total = new Intl.NumberFormat().format(totalLength);
const percent = ((downloadedLength / totalLength) * 100).toFixed(4)
console.log(`downloaded : ${saved} / ${total} ( ${percent} % ) `);
// 取得資料時 , 呼叫回呼函式 cb
cb && cb({downloadedLength, totalLength});
})
.on('write-finish', resolve)
.catch(reject);
})
}
顯示對話框 , 讓使用者決定是否要更新到最新的版本
// 更新對話框
function createUpdateWindow() {
const win = new BrowserWindow({
width: 400,
height: 200,
frame: false, // 標題列不顯示
transparent: true, // 背景透明
autoHideMenuBar: true, // 工具列不顯示
webPreferences: {
nodeIntegration: true,
},
});
win.loadFile('./update.html');
return win;
}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Update Confirm</title>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@4.5.3/dist/css/bootstrap.min.css">
<style>
body {
user-select: none;
height: 100%;
background-color: #3cc245;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
}
h1 {
margin: 20px;
}
</style>
</head>
<body>
<h1>有新的版本可使用 , 您是否要更新 ?</h1>
<div class="progress" style="width: 150px;display: none">
<div class="progress-bar progress-bar-striped progress-bar-animated"></div>
</div>
<div id="btn-group">
<button class="btn btn-primary" onclick="confirm()">更新</button>
<button class="btn btn-secondary" onclick="cancel()">取消</button>
</div>
<script>
// ...JS code , 可參考 https://github.com/andrew781026/ithome_ironman_2020/blob/master/day-36/update.html
</script>
</body>
</html>
完整下載安裝檔後 , 利用 spawn 執行它
利用昨天整理的 doInstall 函式 , 執行之
如有需求背景執行 , 可追加 args 參數
參數 | 說明 |
---|---|
--updated |
更新模式 |
/S |
背景執行 ( silent mode ) |
--force-run |
安裝完成 , 執行應用程式 |
const doInstall = (exe = 'installer_path', args = ["--updated"]) => {
return new Promise((resolve, reject) => {
const process = spawn(exe, args, {
detached: true, // 讓執行緒與 NodeJS 脫鉤
stdio: "ignore",
})
process.on("error", error => reject(error))
process.unref()
if (process.pid) resolve(true);
})
}
利用 spawn 執行時 , 會出現安裝畫面
之後你利用捷徑 開啟應用程式 , 就會看到更新後的版本了 !
更新
不執行更新
下方為範例的安裝檔
今年小弟第一次參加 `鐵人賽` , 如文章有誤 , 請各位前輩提出指正 , 感謝 <(_ _)>